#!/usr/bin/perl

######################################################################
package Swignition::uF::hAtom;
######################################################################

use CGI::Util;
use Swignition::GenericParser::Utils;
use Swignition::uF;
use Swignition::uF::hEntry;
use XML::LibXML qw(:all);
use strict;


sub consume
{
	my $page   = shift;

	# hAtom	
	unless ( $page->{Profiles}->{'http://purl.org/uF/hAtom/0.1/'}
	||       !$page->{options}->{uf_strict_profiles})
	{
		return;
	}

	$page->{uF}->{hAtom} = [ parse_all($page) ];
	foreach my $hat (@{ $page->{uF}->{hAtom} })
	{
		my $entries = 0;
		foreach my $h ( @{ $hat->{entry} } )
		{
			$entries++;
			$page->mergeSubject( $h->rdf_subject );
		}
		$page->mergeSubject( $hat->rdf_subject )
			if ($entries || !$hat->{'isTheRoot'});
	}

	if (defined $page->{uF}->{hAtom}->[0])
		{ $page->mergeNS( $page->{uF}->{hAtom}->[0] ); }		
}


sub parse_all
{
	my $page   = shift;
	my $within = shift || $page->{DOM};
	my $fullPageMode;
	my @nodes  = searchClass('hfeed', $within);
	my @rv;
	
	if ($within->nodeType == XML::LibXML::XML_DOCUMENT_NODE)
	{
		unshift @nodes, $within->getDocumentElement;
		$fullPageMode = 1;
	}
	
	foreach my $hatom (@nodes)
	{
		my $hatom_parsed = parse($page, $hatom);
		push @rv, $hatom_parsed; 
	}
	
	if ($fullPageMode)
	{
		$rv[0]->{'isTheRoot'} = 1;
		$rv[0]->{_id} = $page->uri;
	}
	
	return @rv;
} #/sub parse_all


sub uri
{
	my $this = shift;
	my $all  = shift;
	my @rv;
	
	if ($this->{_dom}->nodeType == XML_DOCUMENT_NODE
	 || $this->{_dom}->tagName eq 'html'
	 || $this->{_dom}->tagName eq 'body')
	{
			push @rv, $this->{_page}->uri;
	}
	if ($this->{_id} =~ /^http\:/)
	{
		push @rv, $this->{_id};
	}
	elsif (length $this->{_id})
	{
		push @rv, $this->{_page}->uri.'#'.$this->{_id};
	}
	push @rv, Swignition::GenericParser::Utils::BNodeURI($this->{_dom}, 'Feed')
		unless (@rv);
	
	return $rv[0] unless (defined $all);
	while ($all) { shift @rv; $all--; }
	return @rv;
} #/sub uri


sub rdf_subject
{
	my $this    = shift;
	my $ns      = 'http://bblfish.net/work/atom-owl/2006-06-06/#';

	# Create RDF subject
	my $subject = Swignition::RDFModel::Subject->new($this->uri);
	$subject->addObject($this);
	$subject->setDom($this->{_dom});
	
	return $subject unless ($this->{entry});
	
	$subject->addVal('http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
		Swignition::RDFModel::Value->new($ns.'Feed', 1));

	foreach my $V (@{ $this->{category} })
	{
		my $val;
		if (ref $V)
			{ $val = Swignition::RDFModel::Value->new($V->uri, 1); }
		else
			{ $val = Swignition::RDFModel::Value->new($V); }
		$subject->addVal($ns.'category', $val);
	}

	foreach my $V (@{ $this->{entry} })
	{
		my $val;
		if (ref $V)
			{ $val = Swignition::RDFModel::Value->new($V->uri, 1); }
		else
			{ $val = Swignition::RDFModel::Value->new($V); }
		$subject->addVal($ns.'entry', $val);
	}

	return $subject;
} #/sub rdf_subject


sub metadata_ns
{
	my $this = shift;
	# We use an IETF URN namespace because although we reuse the semantics of
	# http://www.w3.org/2005/Atom, we can't guarantee that output from this
	# module will conform to the Atom spec (e.g. content model for various
	# elements).
	return { 
		'awol'  => { nsurl=>'http://bblfish.net/work/atom-owl/2006-06-06/#',title=>'Atom OWL' },
		'awolx' => { nsurl=>'http://buzzword.org.uk/rdf/atomix#',title=>'Atom Threading' }
	};
} #/sub metadata_ns


sub parse
{
	my $page = shift;
	my $rv = { '_dom'=>shift };
	
	if ('XML::LibXML::Document' eq ref $rv->{_dom})
	{
		$rv->{_dom} = $rv->{_dom}->documentElement;
	}
	
	my $pkg = __PACKAGE__;
	if (defined $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') })
		{ return $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') }; }
	else
		{ $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') } = $rv; }
	
	my $root_node = $rv->{'_dom'}->cloneNode(1);

	if ('XML::LibXML::Element' eq ref $rv->{_dom})
	{
		my $id = $root_node->getAttribute('id');
		$rv->{_id} = $id if (length $id);
	}
	$rv->{_page} = $page;
	
	data_patterns($page, $root_node);
	
	# Pull out nested hEntrys
	my @E = Swignition::uF::hEntry::parse_all($page, $root_node);
	$rv->{entry} = \@E unless (!@E);

	# If no entries, then we're not really a feed, so don't both with the other
	# stuff.
	if ($rv->{entry})
	{
		# Now that we have reached here, no other composite microformats have
		# any business being nested within this root element. So in the interest
		# of Microformat opacity, let's destroy them.
		destroyer($root_node, 'vcard');  # Save hCards!
		
		# Author
		my @nested = searchClass('vcard', $root_node);
		foreach my $h (@nested)
		{
			if ($h->getAttribute('class') =~ / (^|\s) author (\s|$) /x)
			{
				push @{ $rv->{author} }, Swignition::uF::hCard::parse($page, $h);
			}
		}	
		if (!defined $rv->{author})
		{
			foreach my $h (@nested)
			{
				if ($h->tagName eq 'address')
				{
					push @{ $rv->{author} }, Swignition::uF::hCard::parse($page, $h);
				}
			}
			if (!defined $rv->{author})
			{
				foreach my $address ($root_node->getElementsByTagName('address'))
				{
					push @{ $rv->{author} }, Swignition::uF::hCard::parse_all($page, $address);
				}
			}
		}
		# TODO - nearest in parent?
		
		# Categories
		my @tags = Swignition::uF::RelTag::parse_all($page, $root_node);
		$rv->{category} = \@tags unless (!@tags);	
		
		# Null entry authors should be set to feed author.
		foreach my $e (@{$rv->{entry}})
		{
			$e->{author} = $rv->{author} unless ($e->{author});
		}
		
	}
	
	bless $rv;
	return $rv;
} #/sub parse


1; #/package Swignition::uF::hAtom




